﻿#include "Syntax/Object/Object.hpp"
#include <Syntax/LexicalScanner.hpp>
#include <Syntax/Object/Boolean.hpp>
#include <Syntax/Object/Integer.hpp>
#include <Syntax/Object/String.hpp>

#include <cstdint>
#include <cstdlib>
#include <stdio.h>
#include <string>

SharedToken LexicalScanner::NextToken()
{
    // 令牌开始位置;
    size_t start            = this->GetCurrentPosition();
    // 获取一个字符;
    char currentChar        = this->NextChar();
    // 当前令牌:默认 未知;
    SyntaxKind currentKind  = SyntaxKind::Unkown;

    // object;
    SharedObject tokenValue = nullptr;

    do
    {
        switch (currentChar)
        {
        case 0 :
        {
            return std::make_shared<SyntaxToken>(SyntaxKind::Eof, "", nullptr, start);
        }
        case '(' :
        {
            currentKind = SyntaxKind::OpenParenthesis;
            break;
        }
        case ')' :
        {
            currentKind = SyntaxKind::CloseParenthesis;
            break;
        }
        case '[' :
        {
            currentKind = SyntaxKind::OpenBracket;
            break;
        }
        case ']' :
        {
            currentKind = SyntaxKind::CloseBracket;
            break;
        }
        case '{' :
        {
            currentKind = SyntaxKind::OpenBrace;
            break;
        }
        case '}' :
        {
            currentKind = SyntaxKind::CloseBrace;
            break;
        }
        case ';' :
        {
            currentKind = SyntaxKind::Semicolon;
            break;
        }
        case '.' :
        {
            currentKind = SyntaxKind::Dot;
            break;
        }
        case ',' :
        {
            currentKind = SyntaxKind::Coma;
            break;
        }
        case ':' :
        {
            currentKind = SyntaxKind::Colon;
            break;
        }
        case '~' :
        {
            currentKind = SyntaxKind::Tilde;
            break;
        }
        case '^' :
        {
            currentKind = SyntaxKind::Hat;
            break;
        }
        case '+' :
        {
            currentKind = SyntaxKind::Plus;
            break;
        }
        case '-' :
        {
            currentKind = SyntaxKind::Minus;
            break;
        }
        case '*' :
        {
            currentKind = SyntaxKind::Star;
            break;
        }
        case '/' :
        {
            currentKind = SyntaxKind::Slash;
            // 注释;
            if (this->ReadComments(currentChar))
                currentKind = SyntaxKind::Comments;
            break;
        }
        case '%' :
        {
            currentKind = SyntaxKind::Mod;
            break;
        }
        case '!' :
        {
            currentKind = SyntaxKind::Bang;
            if (this->Current() == '=')
            {
                currentKind = SyntaxKind::BangEquals;
                this->Advance();
            }
            break;
        }
        case '=' :
        {
            currentKind = SyntaxKind::Assign;
            if (this->Current() == '=')
            {
                currentKind = SyntaxKind::Equals;
                this->Advance();
            }

            break;
        }
        case '|' :
        {
            currentKind = SyntaxKind::Pipe;
            if (this->Current() == '|')
            {
                currentKind = SyntaxKind::PipePipe;
                this->Advance();
            }
            break;
        }
        case '&' :
        {
            currentKind = SyntaxKind::Ampersand;
            if (this->Current() == '&')
            {
                currentKind = SyntaxKind::AmpersandAmpersand;
                this->Advance();
            }
            break;
        }
        case '<' :
        {
            currentKind = SyntaxKind::Less;
            if (this->Current() == '=')
            {
                currentKind = SyntaxKind::LessOrEquals;
                this->Advance();
            }
            break;
        }
        case '>' :
        {
            currentKind = SyntaxKind::Greater;
            if (this->Current() == '=')
            {
                currentKind = SyntaxKind::GreaterOrEquals;
                this->Advance();
            }
            break;
        }
        case '"' :
        {
            currentKind = SyntaxKind::String;
            tokenValue  = Object::NewString(this->ReadStrings());
            break;
        }
        case '0' :
        case '1' :
        case '2' :
        case '3' :
        case '4' :
        case '5' :
        case '6' :
        case '7' :
        case '8' :
        case '9' :
        {
            int64_t value = this->ReadNumbers();
            // this->Retreat();
            currentKind   = SyntaxKind::Number;
            tokenValue    = Object::NewInt(value);
            break;
        }
        case ' ' :
        case '\t' :
        case '\n' :
        case '\r' :
        {
            // 空白符;
            while (this->IsWhitespace(this->NextChar()))
                ;
            this->Retreat();
            currentKind = SyntaxKind::Whitespace;
            break;
        }
        default :
        {
            // 标识符;
            if (this->isIdentifier(currentChar))
            {
                currentChar = this->NextChar();

                while (this->IsDigit(currentChar) || this->isIdentifier(currentChar))
                {
                    currentChar = this->NextChar();
                }
                this->Retreat();

                // 判断 关键字;
                std::string tokenText = this->GetCurrentTokenString(start);

                currentKind           = this->allKeyword.isKeyWord(tokenText);

                // bool type;
                if ((currentKind == SyntaxKind::True) || (currentKind == SyntaxKind::False))
                {
                    tokenValue = Object::NewBool(currentKind == SyntaxKind::True);
                }
                if (currentKind == SyntaxKind::Unkown)
                    currentKind = SyntaxKind::Identifier;
                break;
            }

            this->diagnostics->ReportBadCharacter(start, currentChar);
            break;
        }
        }
    } while (0);

    // 令牌文本长度;
    std::string tokenText = this->GetCurrentTokenString(start);
    return std::make_shared<SyntaxToken>(currentKind, tokenText, tokenValue, start);
}
char LexicalScanner::NextChar()
{
    if (this->currentCodeIndex > this->codeTotalLength)
    {
        return 0;
    }
    return this->Text[this->currentCodeIndex++];
};

char LexicalScanner::Current()
{
    if (this->currentCodeIndex > this->codeTotalLength)
    {
        return 0;
    }
    return this->Text[this->currentCodeIndex];
};

bool LexicalScanner::ReadComments(char ch)
{
    size_t start = GetCurrentPosition() - 1;
    if (ch != '/')
        return false;
    char currChar = this->Current();
    // 单行;
    if (currChar == '/')
    {
        do
        {
            currChar = NextChar();
        } while ((currChar != '\n') && (currChar != 0));

        // 设置行列;
        return true;
    }
    // 多行;
    if (currChar == '*')
    {
        do
        {
            currChar = NextChar();
        } while ((currChar != 0) && !((currChar == '*') && (Current() == '/')));
        if (currChar == 0)
        {
            this->diagnostics->ReportUnexpectedKindError(
                TextSpan(start, this->GetCurrentPosition() - start), SyntaxKind::Comments, "Ending character('*/') not found");
        }
        NextChar();
        return true;
    }
    return false;
}

int64_t LexicalScanner::ReadNumbers()
{
    // 跳到上一个字符;
    this->Retreat();

    std::string sb = "";
    char        cc = this->Current();

    while (this->IsDigit(cc))
    {
        this->Advance();
        sb += cc;
        cc = this->Current();
    }
    // 转换成整数;
    return std::atoi(sb.c_str());
}

std::string LexicalScanner::ReadStrings()
{
    std::string sb   = "";
    bool        done = false;
    while (!done)
    {
        switch (this->Current())
        {
        case '\0' :
        case '\r' :
        case '\n' :
        {
            done = true;
            TextSpan span{this->GetCurrentPosition(), 1};
            diagnostics->ReportUnterminatedString(span);
            break;
        }
        case '"' :
            this->Advance();
            if (this->Current() == '"')
            {
                sb += this->NextChar();
            }
            else
            {
                done = true;
            }
            break;
        default :
            sb += this->NextChar();
        }
    }
    return sb;
}